require 'depac-common/softlayer'
require 'depac-deployment/environment_configuration'
require 'depac-deployment/helper'
require 'depac-deployment/configure'
require 'depac-deployment/provision'
require 'depac-common/constants'
require 'yaml'
require 'fileutils'

#module CloudDeployment
  def updateDatabase(targetEnvConfigObject, buildLabel, updateFS, timestamp)
    # construct the configuration file from target environment configuration
    dbUpdateEnvConfig = constructConfig(targetEnvConfigObject)
    dbUpdateEnvConfigObject = EnvironmentConfiguration.new(dbUpdateEnvConfig)
    
    # provision instance based on the input configuration
    nodeInfoMap = provisionInstance(dbUpdateEnvConfigObject, buildLabel, dbUpdateEnvConfigObject.getEnvironmentName(), 0, targetEnvConfigObject)
    updateOutputWithNodeInformation(dbUpdateEnvConfigObject, nodeInfoMap)
    
    # the ip addresses and info populated in this map will be used for configuring
    nodeInfoParamMap = buildNodeInfoParamMap(targetEnvConfigObject)
    
    # construct original db hostname - 
	# fix for POOL_CONFIG in PLT_DB_POOL table containing old hostname for pool initialization
	oldDBInfo = dbOldHostnameGen(targetEnvConfigObject)
	envconfig = targetEnvConfigObject.getEnvironmentConfiguration()
	envconfig["oldDBInfo"]=oldDBInfo   
    
    # configure instance
    configureInstance(nodeInfoMap, nodeInfoParamMap, targetEnvConfigObject.getEnvironmentConfiguration())
    
    startUpdateDBTime = Time.new
    
    # update database schema
    updateDatabaseSchema(targetEnvConfigObject, nodeInfoMap, timestamp)
    
    # update factory setup data
    if(updateFS)
      updateFSData(targetEnvConfigObject, nodeInfoMap, timestamp)
    end
    
    endUpdateDBTime = Time.new
    
    Depac::Log.write("INFO", "Time taken to update database: #{computeDuration(endUpdateDBTime - startUpdateDBTime)}")
    Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
    
    Depac::Log.write("INFO", "De-provisioning foundation install CCIs...")
    Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
    
    # de-provision the instances provisioned to perform db update
    deleteEnvironment(Depac::CloudDeploymentConstants::AUTHORING_NODES, dbUpdateEnvConfigObject, Array.new)
    deleteEnvironment(Depac::CloudDeploymentConstants::RUNTIME_NODES, dbUpdateEnvConfigObject, Array.new)
    
    return targetEnvConfigObject
  end
  
  def updateDatabaseSchema(targetEnvConfigObject, nodeInfoMap, timestamp)
    typeArr = getEnvironmentTypes()
    private_key_path = Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH
    
    typeArr.each do |type|
      if(nodeInfoMap[type] != nil)
        nodeInfoMap[type].each do |role, nodeInfoArr|
          Depac::Log.write("INFO", "Updating database schema #{type}:#{role}")
          Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
          
          nodeInfoArr.each do |hostnameIPMap|
            cmdstr = "ssh -t -t -i #{private_key_path} root@#{hostnameIPMap['IP']} -o StrictHostKeyChecking=no 'cd /opt/ssfs/runtime/bin;sudo ./deployer.sh -t entitydeployer -l info'"
            Depac::Log.write("INFO", "Executing command: " + cmdstr)
            output =  `#{cmdstr}`
            Depac::Log.write("INFO", output)
            Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
            
            createDBlogFile(targetEnvConfigObject.getEnvironmentName(), "entitydeployer", output, type, timestamp)
          end
        end
      end
    end
  end
  
  def updateFSData(targetEnvConfigObject, nodeInfoMap, timestamp)
    typeArr = getEnvironmentTypes()
    private_key_path = Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH
    
    typeArr.each do |type|
      if(nodeInfoMap[type] != nil)
        nodeInfoMap[type].each do |role, nodeInfoArr|

          Depac::Log.write("INFO", "Updating factory setup data #{type}:#{role}")
          Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
          
          nodeInfoArr.each do |hostnameIPMap|
            # remove the .restart files            
            cmdstr = "ssh -t -t -i #{private_key_path} root@#{hostnameIPMap['IP']} -o StrictHostKeyChecking=no 'cd /opt/ssfs/runtime/bin;./loadCartDefaults.sh'"
            Depac::Log.write("INFO", "Executing command: " + cmdstr)
            output =  `#{cmdstr}`
            Depac::Log.write("INFO", output)
            Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
            
            createDBlogFile(targetEnvConfigObject.getEnvironmentName(), "loadDefaults", output, type, timestamp)
          end
        end
      end
    end
  end
  
  def createDBlogFile(environmentName, commandName, output, type, timestamp)
    dbLogFilePath = File.expand_path("~/depac-deployment/db_logs")
    FileUtils.mkdir_p "#{dbLogFilePath}"
    fileName = "#{environmentName}_#{type}_#{commandName}_#{timestamp}"
    File.open("#{dbLogFilePath}/#{fileName}.log","w") {|f|
      f.puts(output)
      f.close
    }
  end
  
  def constructConfig(targetEnvConfigObject)
  	targetConfObj = targetEnvConfigObject.getEnvironmentConfiguration()
  	envConfig = {
        'environment' => targetEnvConfigObject.getEnvironmentName() }
     
  	if (targetConfObj['authoring']!=nil)
  		envConfig['authoring'] = [
        {
          'role' => 'agent',   
          'agent_nodes' => [{'server_name' => 'dbupdate'}],
          'vmconfig' => targetConfObj['defaultVMConfig']
        },
        ]
  	end 
  	if (targetConfObj['runtime']!=nil)
  		envConfig['runtime'] = [
        {
          'role' => 'agent',   
          'agent_nodes' => [{'server_name' => 'dbupdate'}],
          'vmconfig' => targetConfObj['defaultVMConfig']
        },
        ]
  	end  
      return envConfig
  end
  
  def changePowerStateOfApplicationInstances(targetEnvConfigObject, powerState)
    roleArr = getRolesToChangePowerState
    typeArr = getInstanceTypesToChangePowerState
    
    typeArr.each do |type|
      roleArr.each do |role|
        
        nodeConfig = targetEnvConfigObject.getNodeConfigurationByRoleAndType(role, type)
        if (nodeConfig != nil)
	        nodeInfoArr = nodeConfig['node_info']
	        
	        nodeInfoArr.each do |nodeInfo|
	          vmid = nodeInfo['vmid']
	          hostname = nodeInfo['hostname']
	          ipAddress = nodeInfo['IP']
	            
	          Depac::Log.write("INFO", "-> #{type}:#{role} [host=#{hostname} ip=#{ipAddress}]")
	          $slmgr.changePowerSate(vmid, powerState)
	        end
	    end
      end
    end
  end
  
  # Power Off 
  def powerOffApplicationInstances(targetEnvConfigObject)
    Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
    Depac::Log.write("INFO", "Powering Off Appserver and Agent instances...")
    changePowerStateOfApplicationInstances(targetEnvConfigObject, 'Off')
  end
  
  # Power On
  def powerOnApplicationInstances(targetEnvConfigObject)
    Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
    Depac::Log.write("INFO", "Powering On Appserver and Agent instances...")
    changePowerStateOfApplicationInstances(targetEnvConfigObject, 'On')
  end
  
  def getRolesToChangePowerState
    roleArr = []
    roleArr << Depac::CloudDeploymentConstants::ROLE_APPSERVER
    roleArr << Depac::CloudDeploymentConstants::ROLE_AGENT
  end
  
  def getInstanceTypesToChangePowerState
    typeArr = []
    typeArr << Depac::CloudDeploymentConstants::AUTHORING_NODES
    typeArr << Depac::CloudDeploymentConstants::RUNTIME_NODES
  end
#end

  
=begin
envConfig = CloudDeployment.constructConfig(nil)
File.open("C:\\abc.yml","w") {|f|
    f.puts(envConfig.to_yaml())
    f.close
  }
=end
#docker poc
def updateDatabaseForDocker(targetEnvConfigObject, buildLabel, updateFS, timestamp)
    # construct the configuration file from target environment configuration
    dbUpdateEnvConfig = constructConfig(targetEnvConfigObject)
    dbUpdateEnvConfigObject = EnvironmentConfiguration.new(dbUpdateEnvConfig)

    Depac::Log.write("INFO", "updateDatabaseForDocker start...")

    # provision instance based on the input configuration
    # nodeInfoMap = provisionInstance(dbUpdateEnvConfigObject, buildLabel, dbUpdateEnvConfigObject.getEnvironmentName(), 0, targetEnvConfigObject)
    
    provisionInstanceForDocker(dbUpdateEnvConfigObject, buildLabel, dbUpdateEnvConfigObject.getEnvironmentName(), 1, targetEnvConfigObject, true)

    nodeInfoMap = buildNodeInfoMap(targetEnvConfigObject,"agent")  
    Depac::Log.write("INFO", "nodeInfoMap:#{nodeInfoMap}")

    # the ip addresses and info populated in this map will be used for configuring
    nodeInfoParamMap = buildNodeInfoParamMap(targetEnvConfigObject)
    Depac::Log.write("INFO", "nodeInfoParamMap :#{nodeInfoParamMap }")


    
    # construct original db hostname - 
	# fix for POOL_CONFIG in PLT_DB_POOL table containing old hostname for pool initialization
	#oldDBInfo = dbOldHostnameGen(targetEnvConfigObject)
	#envconfig = targetEnvConfigObject.getEnvironmentConfiguration()
	#envconfig["oldDBInfo"]=oldDBInfo   
   
    # configure instance

    configureInstanceForDocker(nodeInfoMap, nodeInfoParamMap, targetEnvConfigObject.getEnvironmentConfiguration())
    
    startUpdateDBTime = Time.new
  
    # update database schema
    updateDatabaseSchemaForDocker(targetEnvConfigObject, nodeInfoMap, timestamp)
      
    # update factory setup data
    if(updateFS)
      updateFSDataForDocker(targetEnvConfigObject, nodeInfoMap, timestamp)
    end
    
    endUpdateDBTime = Time.new
    
    Depac::Log.write("INFO", "Time taken to update database: #{computeDuration(endUpdateDBTime - startUpdateDBTime)}")
    Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
    
    Depac::Log.write("INFO", "De-provisioning foundation install CCIs...")
    Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
    
    # de-provision the instances provisioned to perform db update
    #deleteEnvironment(Depac::CloudDeploymentConstants::AUTHORING_NODES, dbUpdateEnvConfigObject, Array.new)
    #deleteEnvironment(Depac::CloudDeploymentConstants::RUNTIME_NODES, dbUpdateEnvConfigObject, Array.new)
    deleteEnvironmentForDocker(nodeInfoMap)
    
    #restore sshPort
    typeArr = getEnvironmentTypes()
    typeArr.each do |type|
        if(nodeInfoMap[type] != nil)
             nodeInfoMap[type].each do |role, nodeInfoArr|
                 nodeInfoArr.each do |hostnameIPMap|
                     hostnameIPMap['sshPort']=hostnameIPMap['sshPort']-1
                 end
             end
        end
    end  
    return targetEnvConfigObject
    
  end

def updateDatabaseSchemaForDocker(targetEnvConfigObject, nodeInfoMap, timestamp)
    typeArr = getEnvironmentTypes()
    private_key_path = Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH
    
    targetEnvConfig = targetEnvConfigObject.getEnvironmentConfiguration()
	
    typeArr.each do |type|
      if(nodeInfoMap[type] != nil)
        nodeInfoMap[type].each do |role, nodeInfoArr|
          Depac::Log.write("INFO", "Updating database schema #{type}:#{role}")
          Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
          
          nodeInfoArr.each do |hostnameIPMap|
            
            sshPort=targetEnvConfig['dockerContainerPort']
            if hostnameIPMap['sshPort'] != nil          
                sshPort=hostnameIPMap['sshPort']
            end
            
            cmdstr = "ssh -t -t -i #{private_key_path} root@#{hostnameIPMap['IP']} -p #{sshPort} -o StrictHostKeyChecking=no 'cd /opt/ssfs/runtime/bin;sudo ./deployer.sh -t entitydeployer -l info'"
            Depac::Log.write("INFO", "Executing command: " + cmdstr)
            output =  `#{cmdstr}`
            Depac::Log.write("INFO", output)
            Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
            
            createDBlogFile(targetEnvConfigObject.getEnvironmentName(), "entitydeployer", output, type, timestamp)
          end
        end
      end
    end
  end

def updateFSDataForDocker(targetEnvConfigObject, nodeInfoMap, timestamp)
    typeArr = getEnvironmentTypes()
    private_key_path = Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH
    
    targetEnvConfig = targetEnvConfigObject.getEnvironmentConfiguration()

    typeArr.each do |type|
      if(nodeInfoMap[type] != nil)
        nodeInfoMap[type].each do |role, nodeInfoArr|

          Depac::Log.write("INFO", "Updating factory setup data #{type}:#{role}")
          Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
          
          nodeInfoArr.each do |hostnameIPMap|
            # remove the .restart files  
            
            sshPort=targetEnvConfig['dockerContainerPort']
            if hostnameIPMap['sshPort'] != nil          
                sshPort=hostnameIPMap['sshPort']
            end
                      
            cmdstr = "ssh -t -t -i #{private_key_path} root@#{hostnameIPMap['IP']} -p #{sshPort} -o StrictHostKeyChecking=no 'cd /opt/ssfs/runtime/bin;sudo ./loadPriceDefaults.sh '"
            Depac::Log.write("INFO", "Executing command: " + cmdstr)
            output =  `#{cmdstr}`
            Depac::Log.write("INFO", output)
            Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
            
            createDBlogFile(targetEnvConfigObject.getEnvironmentName(), "loadDefaults", output, type, timestamp)
          end
        end
      end
    end
  end

def deleteEnvironmentForDocker(nodeInfoMap)
    typeArr = getEnvironmentTypes()
    private_key_path = Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH
    
    typeArr.each do |type|
      if(nodeInfoMap[type] != nil)
        nodeInfoMap[type].each do |role, nodeInfoArr|

          Depac::Log.write("INFO", "Updating factory setup data #{type}:#{role}")
          Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
          
          nodeInfoArr.each do |hostnameIPMap|
              #1.clean docker container
              if hostnameIPMap['dockerID'] !=nil
		        cmdstr= "ssh -t -t -i #{private_key_path} root@#{hostnameIPMap['IP']} -o StrictHostKeyChecking=no 'sudo docker stop #{hostnameIPMap['dockerID']};sudo docker rm -f #{hostnameIPMap['dockerID']}'"
		        Depac::Log.write("INFO", "Executing command: #{cmdstr}")
		        output =  `#{cmdstr}`
		        Depac::Log.write("INFO", "Executing command output: #{output}")
		      end
          end
        end
      end
    end
  end
